package com.hardson.toolkit.util;

import info.monitorenter.cpdetector.io.*;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

public class CharsetDetectorUtil {

    public static Charset detectCharset(final File file) {
        try (InputStream is = new BufferedInputStream(Files.newInputStream(file.toPath()))) {
            // 处理文件的字符集，得到文件的字符集
            //CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();//BUG，会抛出ConcurrentModificationException
            MyCodepageDetectorProxy detector = MyCodepageDetectorProxy.getInstance();
            detector.add(new ParsingDetector(false));
            detector.add(JChardetFacade.getInstance());
            detector.add(ASCIIDetector.getInstance());
            detector.add(UnicodeDetector.getInstance());
            // charset = detector.detectCodepage(file.toURI().toURL());
            return detector.detectCodepage(is, is.available());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 原实现CodepageDetectorProxy会抛出ConcurrentModificationException
     * https://blog.csdn.net/a398942089/article/details/90080247
     *
     * @author hardson
     */
    private static final class MyCodepageDetectorProxy extends AbstractCodepageDetector {

        private static final long serialVersionUID = -7389424614984024701L;

        private final Set<ICodepageDetector> detectors = new LinkedHashSet<>();

        public static MyCodepageDetectorProxy getInstance() {
            return new MyCodepageDetectorProxy();
        }

        public boolean add(final ICodepageDetector detector) {
            return this.detectors.add(detector);
        }

        @Override
        public Charset detectCodepage(final URL url) throws IOException {
            Charset ret = null;
            for (ICodepageDetector detector : this.detectors) {
                ret = detector.detectCodepage(url);
                if (ret == null) {
                    continue;
                }
                if (ret != UnknownCharset.getInstance()) {
                    if (ret instanceof UnsupportedCharset) {
                        // TODO: Debug logging: found illegal charset tag or encoding
                        // declaration.
                    } else {
                        break;
                    }
                }
            }
            return ret;
        }

        @Override
        public Charset detectCodepage(final InputStream in, final int length)
                throws IOException, IllegalArgumentException {

            if (!in.markSupported()) {
                throw new IllegalArgumentException(String.join(in.getClass().getName(), "The given input stream ("
                        , ") has to support for marking."));
            }
            Charset ret = null;
            for (ICodepageDetector detector : this.detectors) {
                in.mark(length);
                ret = detector.detectCodepage(in, length);
                // if more bytes have been read than marked (length) this will throw an
                // exception:
                try {
                    in.reset();
                } catch (IOException ioex) {
                    throw new IllegalStateException(
                            "More than the given length had to be read and the given stream could not be reset. Undetermined state for this detection.",
                            ioex);
                }
                if (ret != null) {
                    if (ret != UnknownCharset.getInstance()) {
                        if (ret instanceof UnsupportedCharset) {
                            // TODO: Debug logging: found illegal charset tag or encoding
                            // declaration.
                        } else {
                            break;
                        }
                    }
                }
            }
            return ret;
        }

        @Override
        public String toString() {
            StringBuilder ret = new StringBuilder();
            Iterator<ICodepageDetector> it = this.detectors.iterator();
            int i = 1;
            while (it.hasNext()) {
                ret.append(i);
                ret.append(") ");
                ret.append(it.next().getClass().getName());
                ret.append("\n");
                i++;
            }
            return ret.toString();
        }

    }
}
